/**
 * kl yy-ds @2021
 */
package com.cxps.server.config;

import com.cxps.server.handler.AuthNoLoginHandler;
import com.cxps.server.handler.AuthNoPermissionHandler;
import com.cxps.server.handler.JwtAuthenticationTokenFilter;
import com.cxps.server.utils.SecurityUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.List;

/**
 * springSecurity 核心配置类
 *
 * @author YouCai.Liu
 * @since 2021/10/24
 * ...
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 授权配置
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // 跨域处理和CSRF攻击（后面细说）
        http.cors().and().csrf().disable();

        // 基于Token 禁用session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 白名单列表
        List<String> whiteList = SecurityUtil.getWhiteList();
        // 请求白名单，也就是不拦截的接口
        if (null != whiteList && !whiteList.isEmpty()) {
            String[] freeApi = new String[whiteList.size()];
            for (int i = 0; i < whiteList.size(); i++) {
                freeApi[i] = whiteList.get(i);
            }
            http.authorizeRequests().antMatchers(freeApi).permitAll();
        }

        // 请求配置
        http.authorizeRequests()
                // 跨域请求会先进行一次options请求,允许访问
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                // 其他任何请求，都需要身份认证
                .anyRequest().authenticated()
                .and()
                // 自定义处理异常
                .exceptionHandling()
                .accessDeniedHandler(new AuthNoPermissionHandler())
                .authenticationEntryPoint(new AuthNoLoginHandler())
                // 添加JWT Filter
                .and().addFilterBefore(new JwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    /**
     * 安全过滤器链，主要用来配置一些匿名访问的接口或者静态资源
     * 比如：图片资源、前端静态资源、后台一些公共的接口登录注册找回密码
     *
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/doc.html",
                "/webjars/**", "/favicon.ico", "/service-worker.js",
                "/swagger-resources/**", "/v2/**");
    }

    /**
     * BCryptPasswordEncoder 加密方式是官方推荐使用的。
     * NoOpPasswordEncoder 则是明文密码，仅保留&测试，极不安全；
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}
